import {
  AfterViewInit,
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  ElementRef,
  forwardRef,
  Input,
  OnChanges,
  OnDestroy,
  Renderer2,
  SimpleChanges,
  ViewChild,
  ViewEncapsulation,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { FocusMonitor } from '@angular/cdk/a11y';
import { Subject } from 'rxjs';
import { animate, group, style, transition, trigger } from '@angular/animations';
import { InputBoolean } from '../utils/api';
import { MotionDuration, MotionFunc, noAnimationToVoid } from '../animation/api';

@Component({
  selector: 'lv-checkbase, [lv-checkbase]',
  exportAs: 'lvCheckbase',
  templateUrl: './checkbase.component.html',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  animations: [
    trigger('checkboxInner', [
      noAnimationToVoid,
      transition(':enter', [
        style({ opacity: 0, transform: 'scale(0.8)' }),
        group([
          animate(`${MotionDuration.SM} ${MotionFunc.EASE}`, style({ transform: 'scale(1)' })),
          animate(`${MotionDuration.SM} ${MotionFunc.EASE_IN_OUT}`, style({ opacity: 1 })),
        ]),
      ]),
      transition(':leave', [
        style({ opacity: 1, transform: 'scale(1)' }),
        group([
          animate(`${MotionDuration.SM} ${MotionFunc.EASE}`, style({ transform: 'scale(0.5)' })),
          animate(`${MotionDuration.SM} ${MotionFunc.EASE_IN_OUT}`, style({ opacity: 0 })),
        ]),
      ]),
    ]),
  ],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef((): typeof CheckbaseComponent => CheckbaseComponent),
      multi: true,
    },
  ],
})
export class CheckbaseComponent implements ControlValueAccessor, OnChanges, AfterViewInit, OnDestroy {
  @Input() lvValue: string | number;
  @Input() @InputBoolean() lvDisabled;
  @Input() @InputBoolean() lvIndeterminate = false; // 是否为半选
  @Input() @InputBoolean() lvControl: boolean = false;
  @Input() lvButtonSize: 'default' | 'small' = 'default';
  @ViewChild('input', { static: false }) input: ElementRef<any>;

  @Input() lvViewType: 'default' | 'button' | 'link' | 'custom' = 'default';

  @ViewChild('baseContentElement', { static: false }) private _baseContentElement: ElementRef;
  public disabled: boolean;

  public groupName: string;
  public checked: boolean = false;
  public type: 'radio' | 'checkbox' = 'radio';
  public select$ = new Subject<CheckbaseComponent>();
  constructor(
    public cdr: ChangeDetectorRef,
    private focusMonitor: FocusMonitor,
    private el: ElementRef,
    private renderer: Renderer2,
  ) {}

  registerOnChange(fn: any): void {
    this._onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this._onTouch = fn;
  }

  ngOnDestroy(): void {
    this.focusMonitor.stopMonitoring(this.el);
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.lvDisabled) {
      this.disabled = this.lvDisabled;
    }
  }

  writeValue(value: boolean): void {
    this.checked = coerceBooleanProperty(value);
    this.select$.next(this);
    this.cdr.markForCheck();
  }

  ngAfterViewInit(): void {
    this.focusMonitor.monitor(this.el, true).subscribe((focus): void => {
      if (focus && this.type === 'radio' && !this.lvControl) {
        this.select$.next(this);
      }
    });
    this.updateContent();
  }

  _click(e: MouseEvent): void {
    if (this.disabled || this.lvControl) {
      return;
    }

    if (this.type === 'checkbox') {
      this.checked = !coerceBooleanProperty(this.checked);
    }

    if (this.type === 'radio') {
      if (this.checked) {
        return;
      } else {
        this.checked = true;
      }
    }
    this._onChange(this.checked);
    this.select$.next(this);

    this.input.nativeElement.focus();
  }

  updateContent(): void {
    if (!this._baseContentElement) {
      return;
    }
    const baseContent = this._baseContentElement.nativeElement;
    if (!baseContent.textContent) {
      this.renderer.setStyle(baseContent, 'display', 'none');
      this.renderer.addClass(this.el.nativeElement, 'lv-checkbase-no-label');
    } else {
      this.renderer.removeStyle(baseContent, 'display');
      this.renderer.removeClass(this.el.nativeElement, 'lv-checkbase-no-label');
    }
  }

  private _onTouch: any = (): void => {};

  private _onChange: any = (): void => {};
}
